1 module recaptcha;
2 
3 import std.utf;
4 import std.string;
5 import std.exception;
6 import vibe.http.client;
7 import vibe.stream.operations;
8 
9 const string API_URL         = "http://www.google.com/recaptcha/api";
10 const string API_SECURE_URL  = "https://www.google.com/recaptcha/api";
11 const string VERIFY_URL      = "http://www.google.com/recaptcha/api/verify";
12 
13 /**
14  * An exception type to distinguish exceptions thrown by this module.
15  */
16 class RecaptchaException : Exception {
17 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure{
18     super(msg, file, line);
19   }
20 }
21 alias RecaptchaException RCX;
22 
23 /**
24  * Generate HTML for reCAPTCHA
25  */
26 string recaptchaHTML(string publicKey, bool useSSL = false){
27 	string html;
28 	string server;
29 
30 	// Raise exception if publickey isn't present
31 	enforceEx!RCX(publicKey, "Tried to use a null publicKey parameter.");
32 	enforceEx!RCX(publicKey != "", "Tried to enter an empty publicKey parameter.");
33 
34 	if (useSSL == true)
35 		server = API_SECURE_URL;
36 	else
37 		server = API_URL;
38 	html = r"<script type='text/javascript' src='" ~ server ~ r"/challenge?k=" ~ publicKey ~ "'></script>";
39 	html ~= r"<noscript><iframe src='" ~ server ~ r"/noscript?k=" ~ publicKey ~ r"' height='300' width='500' frameborder='0'></iframe><br/>";
40   html ~= r"<textarea name='recaptcha_challenge_field' rows='3' cols='40'></textarea>";
41   html ~= r"<input type='hidden' name='recaptcha_response_field' value='manual_challenge'/>";
42 	html ~= r"</noscript>";
43 	return html;
44 }
45 
46 /**
47  * Check whether the answers entered by the users are correct
48  */
49 bool verifyRecaptcha(string privateKey, string ip, string challenge, string response) {
50 	bool successful;
51 	string[] apiResponse;
52 	string error;
53 	string query;
54 	
55 	// Raise exceptions for missing parameters
56 	enforceEx!RCX(privateKey, "Tried to use a null private key parameter.");
57 	enforceEx!RCX(privateKey != "", "Tried to enter an empty privateKey parameter.");
58 	enforceEx!RCX(ip, "Tried to use a null remote IP parameter.");
59 	enforceEx!RCX(ip != "", "Tried to enter an empty remote IP parameter.");
60 	enforceEx!RCX(challenge, "Tried to use a null challenge parameter.");
61 	enforceEx!RCX(challenge != "", "Entered an empty challenge parameter.");
62 	enforceEx!RCX(response, "Tried to use a null response parameter.");
63 	enforceEx!RCX(response != "", "Entered an empty response parameter.");
64 
65 	successful = false;
66 
67 	requestHTTP(VERIFY_URL, 
68 		(scope req) {
69 			string requestBody;
70 
71 			query = "privatekey="~privateKey~"&remoteip="~ip~"&challenge="~challenge~"&response="~response;
72 			
73 			req.method = HTTPMethod.POST;
74 			req.headers["User-agent"] = "reCAPTCHA Vibe.d";
75 
76 			requestBody = query;
77 			req.writeBody(cast(ubyte[]) toUTF8(requestBody), "application/x-www-form-urlencoded;  charset=UTF-8");
78 		},
79 
80 		(scope res) {
81 			apiResponse = splitLines( res.bodyReader.readAllUTF8() );
82 			if ( cast(string) apiResponse[0] == cast(string) "true" )
83 				successful = true;
84 			else
85 				successful = false;
86 		}
87 	);
88 
89 	return (successful);
90 }
91 
92 /**
93  * Check error returned by Recaptcha
94  *
95  * Returns either "true" or an error message
96  */
97 string testRecaptcha(string privateKey, string ip, string challenge, string response) {
98 	bool successful;
99 	string[] apiResponse;
100 	string error;
101 	string query;
102 	
103 	// Raise exceptions for missing parameters
104 	enforceEx!RCX(privateKey, "Tried to use a null private key parameter.");
105 	enforceEx!RCX(privateKey != "", "Tried to enter an empty privateKey parameter.");
106 	enforceEx!RCX(ip, "Tried to use a null remote IP parameter.");
107 	enforceEx!RCX(ip != "", "Tried to enter an empty remote IP parameter.");
108 	enforceEx!RCX(challenge, "Tried to use a null challenge parameter.");
109 	enforceEx!RCX(challenge != "", "Entered an empty challenge parameter.");
110 	enforceEx!RCX(response, "Tried to use a null response parameter.");
111 	enforceEx!RCX(response != "", "Entered an empty response parameter.");
112 
113 	successful = false;
114 
115 	requestHTTP(VERIFY_URL, 
116 		(scope req) {
117 			string requestBody;
118 
119 			query = "privatekey="~privateKey~"&remoteip="~ip~"&challenge="~challenge~"&response="~response;
120 			
121 			req.method = HTTPMethod.POST;
122 			req.headers["User-agent"] = "reCAPTCHA Vibe.d";
123 
124 			requestBody = query;
125 			req.writeBody(cast(ubyte[]) toUTF8(requestBody), "application/x-www-form-urlencoded;  charset=UTF-8");
126 		},
127 
128 		(scope res) {
129 			apiResponse = splitLines( res.bodyReader.readAllUTF8() );
130 			if ( cast(string) apiResponse[0] == cast(string) "true" )
131 				successful = true;
132 			else
133 				successful = false;
134 		}
135 	);
136 
137 	if(successful)
138 		return apiResponse[0];
139 	else
140 		return apiResponse[1];
141 }